package com.syuchen.client.http;

import java.io.IOException;
import java.net.Socket;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 * TCP socket连接池
 */
public class ConnectionPool {


    private static final long IDLE_TIMEOUT = 30*1000; // 30 minute

    private static final Map<String, ConnectionInfo> connectionMap = new ConcurrentHashMap<>();
    private static final ScheduledExecutorService idleCheckExecutor  = Executors.newScheduledThreadPool(1);

    private static class ConnectionInfo {
        Socket socket;
        long lastUsedTimestamp;

        ConnectionInfo(Socket socket, long lastUsedTimestamp) {
            this.socket = socket;
            this.lastUsedTimestamp = lastUsedTimestamp;
        }
    }

    public static synchronized Socket getConnectionSocket(String host, int port) throws IOException {
        String key = host + ":" + port;
        ConnectionInfo connectionInfo = connectionMap.get(key);

        if (connectionInfo != null) {
            connectionInfo.lastUsedTimestamp = System.currentTimeMillis();
            return connectionInfo.socket;
        }

        Socket socket = new Socket(host, port);
        socket.setKeepAlive(true);
        connectionInfo = new ConnectionInfo(socket, System.currentTimeMillis());
        connectionMap.put(key, connectionInfo);

        scheduleIdleCheck(connectionInfo);

        return socket;
    }

    private static void scheduleIdleCheck(ConnectionInfo connectionInfo) {
        idleCheckExecutor.scheduleWithFixedDelay(() -> {
            long currentTime = System.currentTimeMillis();
            if (currentTime - connectionInfo.lastUsedTimestamp > IDLE_TIMEOUT) {
                closeConnection(connectionInfo);
            }
        }, IDLE_TIMEOUT, IDLE_TIMEOUT, TimeUnit.MILLISECONDS);
    }

    private static synchronized void closeConnection(ConnectionInfo connectionInfo) {
        if (connectionInfo != null) {
            try {
                connectionInfo.socket.close();
            } catch (IOException e) {
                // Handle or log the exception
            } finally {
                connectionMap.remove(connectionInfo);
            }
        }
    }

}
